package se.webbzon.boltzmann.menu;

import java.util.ArrayList;
import java.util.Collection;

import se.webbzon.oschi01.worldcamera.WorldCameraPane;

public abstract class MItem {
	
	// A collection of all the listeners to this menu item
	private final Collection<MItemListener> listeners;
	
	// The 2-dimensional depth of this menu item
	private final int depth;
	
	// The position and size of the menu item
	private int x, y, width, height;
	
	// Whatever the menu item has focus and is visible
	private boolean focus, visible;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public MItem() {
		this(0);
	}
	
	public MItem(int depth) {
		this(depth,0,0,0,0);
	}
	
	public MItem(int depth, int x, int y, int width, int height) {
		this.depth = depth;
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
		visible = true;
		listeners = new ArrayList<MItemListener>();
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** This method is invoked when the menu item should be drawn. **/
	public abstract void onDraw(GameMenu menu, WorldCameraPane hud);
	
	/** This method is invoked when the menu item is pressed. **/
	public abstract void onPress(GameMenu menu, int x, int y);
	
	/** This method is invoked when the menu item is released. **/
	public abstract void onRelease(GameMenu menu, int x, int y);
	
	/** Returns true if this menu item is draggable. **/
	public abstract boolean isDraggable();
	
	/** Returns true if this menu item can be focused. **/
	public abstract boolean isFocusable();
	
	/** Adds a listener to this menu item. **/
	public boolean addListener(MItemListener listener) {
		return listeners.add(listener);
	}
	
	/** Removes a listener from this menu item. **/
	public boolean removeListener(MItemListener listener) {
		return listeners.remove(listener);
	}
	
	/** Clears all the listeners from this menu item. **/
	public void clearListeners() {
		listeners.clear();
	}
	
	/** Returns the 2-dimensional depth of this menu item. **/
	public int getDepth() {
		return depth;
	}
	
	/** Sets the visibility of this menu item. **/
	public void setVisible(boolean visible) {
		this.visible = visible;
	}
	
	/** Returns the visibility of this menu item. **/
	public boolean isVisible() {
		return visible;
	}
	
	/** This method is invoked when the menu item is dragged. This method
	 * should be override by the subclass if isDraggable returns true. **/
	public void onDrag(GameMenu menu, int x, int y) {};
	
	/** Returns the horizontal position for where the draggable menu item
	 * has been dragged to. **/
	public int getDragX() { return 0; }
	
	/** Returns the vertical position for where the draggable menu item
	 * has been dragged to. **/
	public int getDragY() { return 0; }
	
	/** Returns true if the given position is within the menu item. **/
	public boolean isWithin(int x, int y) {
		if (x >= this.x && y >= this.y) {
			return (x < this.x + width && y < this.y + height);
		} else
			return false;
	}
	
	/** Returns true if the menu item has focus. **/
	public boolean hasFocus() {
		return focus;
	}
	
	/** Sets whatever the menu item has focus. **/
	public void setFocus(boolean focus) {
		this.focus = focus;
	}
	
	/** Returns the x-position of the menu item. **/
	public int getX() {
		return x;
	}

	/** Sets the x-position of the menu item. **/
	public void setX(int x) {
		this.x = x;
	}

	/** Returns the y-position of the menu item. **/
	public int getY() {
		return y;
	}

	/** Sets the x-position of the menu item. **/
	public void setY(int y) {
		this.y = y;
	}

	/** Returns the width of the menu item. If the width is
	 * negative then it is treated as positive infinity. **/
	public int getWidth() {
		return width;
	}

	/** Sets the width of the menu item. If the width is
	 * negative then it is treated as positive infinity. **/
	public void setWidth(int width) {
		this.width = width;
		notifyListeners();
	}

	/** Returns the height of the menu item. If the height is
	 * negative then it is treated as positive infinity. **/
	public int getHeight() {
		return height;
	}

	/** Sets the width of the menu item. If the height is
	 * negative then it is treated as positive infinity. **/
	public void setHeight(int height) {
		this.height = height;
		notifyListeners();
	}
	
	/*============================================================
	Private Methods
	============================================================*/
	
	/** Notifies to all the listeners that this menu item has been changed. **/
	private final void notifyListeners() {
		for (MItemListener listener : listeners.toArray(new MItemListener[0]))
			listener.onChangeSize(this);
	}

}
